﻿#include "CabinetManager.h"
#include <QUdpSocket>
#include <QtConcurrent/QtConcurrent>
#include <QSqlError>
#include <cmath>
#include <algorithm>

#include "CDBConnector.h"
#include "glog/logging.h"
#include "zlbase/CDeviceBuilderProducer.h"
#include "zlbase/controllor/IDoorControlBoard.h"
#include "zlbase/transport/ICommonDCBTransport.h"
#include "zlbase/datalink/IBaseDatalink.h"

using namespace zl;
using zl::zlbase::ICommonDCBTransport;
using zl::zlbase::IBaseDatalink;
using zl::zlbase::IDoorControlBoard;

#define DEFAULT_LOCALPORT		30000
#define DEFAULT_PCBPORT			30000
#define DEFAULT_BROADCASTADDR	"192.168.1.255"

CCabinetManager* CCabinetManager::instance_ = nullptr;

CCabinetManager * zl::CCabinetManager::Instance()
{
	if (instance_ == nullptr)
	{
		LOG(WARNING) << "Create CabinetManager instance...";
		instance_ = new CCabinetManager;
	}

	return instance_;
}

void zl::CCabinetManager::Release()
{
    LOG(WARNING) << "Release CabinetManager instance...";
	instance_->deleteLater();
	instance_ = nullptr;
}

CCabinetManager::CCabinetManager(QObject * parent)
    : work_thread_(nullptr)
    , work_flag_(false)
{
    start_work_thread();
}

CCabinetManager::~CCabinetManager()
{
    work_flag_ = false;
    if (work_thread_ != nullptr && work_thread_->joinable())
    {
        work_thread_->join();
    }
    delete work_thread_;
    work_thread_ = nullptr;
}

int32_t zl::CCabinetManager::ResetZLBaseConfig(const ZLBaseConfig &cfg)
{
    mutex_lockers_.lock();
    LOG(INFO) << "reset zlbase config";
    lockers_.clear();

    // 根据布局信息生成格口对象
    int32_t boxno(1);
    for (auto& l : cfg.layout_list)
    {
        for (size_t i = 0; i < l.typestr.size(); ++i)
        {
            zl::Locker locker;
            locker.boxno = boxno++;
            locker.type = convert_layout_typestr(l.typestr[i]);
            locker.status = 0x00;
            locker.index = 0;
            locker.pcbno = 0;
            lockers_.push_back(locker);
        }

        auto lt = l.typestr;
        std::transform(lt.begin(), lt.end(), lt.begin(), ::toupper);
    }

    // 设置格口板号和序号
    size_t tmp_index(0);
    for (auto& dcb : cfg.dcb_list)
    {
        // build lockers
        for(int32_t i = 0; i < dcb.count; ++i)
        {
            if (tmp_index >= lockers_.size()) break;
            lockers_[tmp_index].pcbno = dcb.col;
            lockers_[tmp_index].index = i + 1;
            tmp_index += 1;
        }
    }

    mutex_lockers_.unlock();
    // 根据配置信息创建开门板控制对象
    create_door_control_board_devices(cfg.dcb_list);

    // 保存状态
    clean_locker_status_data();
    save_locker_status_data();

    return zl::EResult_Success;
}

int32_t zl::CCabinetManager::SetLockerStatus(int32_t boxno, ELockerStatus index, bool flag)
{
    std::unique_lock<std::mutex> locker(mutex_lockers_);
	LOG(INFO) << "set locker status: " << boxno << " " << index;

    for (size_t i = 0; i < lockers_.size(); ++i)
	{
		Locker& l = lockers_[i];
		if (l.boxno != boxno)
		{
			continue;
		}

		auto s = calc_locker_status(l.status, index, flag);
		if (s != l.status)
		{
			emit lockerStatusChanged(l.boxno);
		}

		l.status = s;
		// save to db
		LOG(INFO) << "update locker status:" << l.boxno << " " << l.status;
		QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();
		query.prepare("UPDATE T_BOX_STATUS SET STATUS=:status WHERE BOXNO=:boxno");
		query.bindValue(":status", l.status);
		query.bindValue(":boxno", l.boxno);

		auto ret = zl::CDBConnector::Instance()->ExecQuery(query);
		if (ret != zl::CDBConnector::EDBError_Success)
		{
			LOG(ERROR) << "update locker status failed:" << l.boxno << " " << l.status;
		}

		break;
	}
	return EResult_Success;
}

int32_t zl::CCabinetManager::GetLockerStatus(int32_t boxno)
{
    std::unique_lock<std::mutex> locker(mutex_lockers_);
	int32_t status = 0x00;
	std::find_if(lockers_.begin(), lockers_.end(), [boxno, &status](const Locker& l)->bool {
		if (l.boxno == boxno)
		{
			status = l.status;
			return true;
		}

		return false;
	});

	LOG(INFO) << "get locker status: " << boxno << " status: " << status;
	return status;
}

int32_t zl::CCabinetManager::GetAllLockers(LockerVec & vec)
{
    std::unique_lock<std::mutex> locker(mutex_lockers_);
	vec = lockers_;
	return lockers_.size();
}

int32_t zl::CCabinetManager::GetLockersByType(LockerVec & vec, ELockerType type, int32_t mask)
{
    std::unique_lock<std::mutex> locker(mutex_lockers_);
    for (auto& l : lockers_)
    {
        if (l.type == type && (l.status & mask) == 0x00)
        {
            vec.push_back(l);
        }
    }

	return vec.size();
}

int32_t zl::CCabinetManager::GetAvilableLocker(ELockerType type, int32_t mask)
{
    std::unique_lock<std::mutex> locker(mutex_lockers_);
	LOG(INFO) << "get available locker: " << type;
    static int32_t index = 0;
	for (size_t i = 1; i <= lockers_.size(); ++i)
	{
		index += 1;
        if ((size_t)index >= lockers_.size())
		{
			index %= lockers_.size();
		}

		const Locker& l = lockers_[index];
		if (type != ELockerType_Default && type != l.type)
		{
			continue;
		}

		if ((l.status & mask) == 0x00) 
		{
			return l.boxno;
		}
	}

	return -1;
}

int32_t zl::CCabinetManager::OpenBox(int32_t boxno)
{
    LOG(INFO) << "open box: " << boxno;

    // 根据箱号获取格口的配置数据
    auto locker = get_locker(boxno);

    // 查找对应的开门板控制接口
    for (auto& d : device_list_)
    {
        if (d->GetCol() == locker.pcbno)
        {
            auto ret = d->OpenLocker(locker.index);
            if (ret == 1)
            {
                return EResult_Success;
            }
            break;
        }
    }

    return EResult_Failed;
}

int32_t zl::CCabinetManager::OpenAll()
{
	LOG(INFO) << "open all";
	std::for_each(lockers_.begin(), lockers_.end(), [&](const Locker& l) {
		OpenBox(l.boxno);
	});

	return EResult_Success;
}

int32_t zl::CCabinetManager::CleanBox(int32_t boxno)
{
	LOG(INFO) << "clean box: " << boxno;
	SetLockerStatus(boxno, ELockerStatus_ALL, false);
	emit lockerStatusChanged(boxno);
	return EResult_Success;
}

int32_t zl::CCabinetManager::CleanAll()
{
	LOG(WARNING) << "clean all locker";
	emit lockerStatusChanged(0);
	return int32_t();
}

int32_t zl::CCabinetManager::LockBox(int32_t boxno)
{
	LOG(INFO) << "lock box: " << boxno;
	SetLockerStatus(boxno, ELockerStatus_Locked, true);
	return int32_t();
}

int32_t zl::CCabinetManager::UnlockBox(int32_t boxno)
{
	LOG(INFO) << "unlock box: " << boxno;
	SetLockerStatus(boxno, ELockerStatus_Locked, false);
	return int32_t();
}

int32_t zl::CCabinetManager::RegisterManagerCard(const std::string & cardno)
{
	LOG(INFO) << "register manager card: " << cardno;

    // check record
    {
        QSqlQuery q1 = zl::CDBConnector::Instance()->CreateQuery();
        q1.prepare("SELECT * FROM T_MANAGER_CARD WHERE CARDNO=:cardno");
        q1.bindValue(":cardno", cardno.c_str());
        auto r1 = zl::CDBConnector::Instance()->ExecQuery(q1);
        if (r1 != EResult_Success)
        {
            LOG(ERROR) << "query manager card failed: " << r1;
            return r1;
        }
        if (q1.next())
        {
            LOG(ERROR) << "cardno is already registered";
            return EResult_Failed;
        }
    }

    // register cardno
	QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();
	query.prepare("INSERT INTO T_MANAGER_CARD (CARDNO, STATUS) VALUES(?, ?)");
	query.bindValue(0, cardno.c_str());
    query.bindValue(1, 2);

	int32_t ret = zl::CDBConnector::Instance()->ExecQuery(query);
	if (ret != zl::CDBConnector::EDBError_Success)
	{
		LOG(ERROR) << "register manager card failed, exec query failed: " << ret;
		return EResult_Failed;
	}

	return EResult_Success;
}

int32_t zl::CCabinetManager::VerifyManagerCard(const std::string & cardno)
{
	LOG(INFO) << "verify manager card: " << cardno;
	QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();
	query.prepare("SELECT CARDNO, STATUS FROM T_MANAGER_CARD WHERE CARDNO=?");
	query.bindValue(0, cardno.c_str());

	int32_t ret = zl::CDBConnector::Instance()->ExecQuery(query);
	if (ret != zl::CDBConnector::EDBError_Success)
	{
		LOG(ERROR) << "verify manager card failed, exec failed: " << ret;
        return -1;
	}

    if (query.first() && query.value("STATUS").toInt() == ECardStatus_Root)
    {
        LOG(INFO) << "verify manager card root access " << query.value("STATUS").toInt();
        return ECardStatus_Root;
    }

    else if (query.first() && query.value("STATUS").toInt() == ECardStatus_Normal)
    {
        LOG(INFO) << "verify manager card normal access " << query.value("STATUS").toInt();
        return ECardStatus_Normal;
    }

    else if (query.first() && query.value("STATUS").toInt() == ECardStatus_Locked)
    {
        LOG(INFO) << "manager card locked, can not access " << query.value("STATUS").toInt();
        return ECardStatus_Locked;
    }

    else
	{
		LOG(INFO) << "manager card not found or in invalid status: " << query.value("STATUS").toInt();
        return -1;
	}

	return EResult_Success;
}

int32_t zl::CCabinetManager::UpdateManagerCardStatus(const std::string & cardno, int32_t status)
{
	LOG(INFO) << "update manager card status: " << cardno << " " << status;
	QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();
	query.prepare("UPDATE T_MANAGER_CARD SET STATUS=? WHERE CARDNO=?");
	query.bindValue(0, status);
	query.bindValue(1, cardno.c_str());

	int32_t ret = zl::CDBConnector::Instance()->ExecQuery(query);
	if (ret != zl::CDBConnector::EDBError_Success)
	{
		LOG(ERROR) << "update manager card failed, exec failed: " << ret;
		return EResult_Failed;
	}

	return EResult_Success;
}

int32_t zl::CCabinetManager::DeleteManagerCard(const std::string & cardno)
{
	LOG(INFO) << "delete manager card: " << cardno;
	QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();
	query.prepare("DELETE FROM T_MANAGER_CARD WHERE CARDNO=?");
	query.bindValue(0, cardno.c_str());

	int32_t ret = zl::CDBConnector::Instance()->ExecQuery(query);
	if (ret != zl::CDBConnector::EDBError_Success)
	{
		LOG(ERROR) << "delete manager card failed, exec failed: " << ret;
		return EResult_Failed;
	}
	
	return EResult_Success;
}

int32_t zl::CCabinetManager::GetManagerCardList(ManagerCardVec & vec)
{
	LOG(INFO) << "get manager card list";
	QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();
	query.prepare("SELECT CARDNO, STATUS FROM T_MANAGER_CARD");
	
	int32_t ret = zl::CDBConnector::Instance()->ExecQuery(query);
	if (ret != zl::CDBConnector::EDBError_Success)
	{
		LOG(ERROR) << "get manager list, exec query failed: " << ret;
		return EResult_Success;
	}

	while (query.next())
	{
		ManagerCard card;
		card.cardno = query.value("CARDNO").toString().toStdString();
		card.status = query.value("STATUS").toInt();

		vec.push_back(card);
	}

	return EResult_Success;
}

void zl::CCabinetManager::load_locker_status_data()
{
	LOG(INFO) << "load locker status";
	QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();
	query.prepare("SELECT BOXNO, PCBNO, SEQ, TYPE, STATUS FROM T_BOX_STATUS ORDER BY BOXNO ASC");
	int32_t ret = zl::CDBConnector::Instance()->ExecQuery(query);
	if (ret != CDBConnector::EDBError_Success)
	{
		LOG(ERROR) << "load cabinet box status failed: " << ret;
		return;
	}

    std::unique_lock<std::mutex> locker(mutex_lockers_);
	lockers_.clear();
	while (query.next())
	{
		Locker l;
		l.boxno = query.value("BOXNO").toInt();
		l.pcbno = query.value("PCBNO").toInt();
		l.index = query.value("SEQ").toInt();
		l.type = query.value("TYPE").toInt();
		l.status = query.value("STATUS").toInt() & (~(zl::ELockerStatus_Door | zl::ELockerStatus_Item));

		lockers_.push_back(l);
	}

	LOG(INFO) << "load locker status data success, count: " << lockers_.size();
}

int32_t zl::CCabinetManager::save_locker_status_data()
{
	LOG(INFO) << "save locker status";
	QSqlQuery query = zl::CDBConnector::Instance()->CreateQuery();
	query.prepare("INSERT INTO T_BOX_STATUS (BOXNO, PCBNO, SEQ, TYPE, STATUS) VALUES(?, ?, ?, ?, ?)");

    std::unique_lock<std::mutex> locker(mutex_lockers_);
	QVariantList list1, list2, list3, list4, list5;
	std::for_each(lockers_.begin(), lockers_.end(), [&](const Locker& l) {
		list1.append(l.boxno);
		list2.append(l.pcbno);
		list3.append(l.index);
		list4.append(l.type);
		list5.append(l.status & (~(zl::ELockerStatus_Door | zl::ELockerStatus_Item)));
	});
	query.addBindValue(list1);
	query.addBindValue(list2);
	query.addBindValue(list3);
	query.addBindValue(list4);
	query.addBindValue(list5);

	auto ret = zl::CDBConnector::Instance()->ExecBatchQuery(query);
	if (ret != zl::CDBConnector::EDBError_Success)
	{
		LOG(ERROR) << "save locker status failed: " << ret;
		return EResult_Failed;
	}

	LOG(INFO) << "save locker status data success, count: " << lockers_.size();
	return EResult_Success;
}

void zl::CCabinetManager::clean_locker_status_data()
{
    LOG(WARNING) << "clean all cabinet data";
    auto ret = zl::CDBConnector::Instance()->ExecSql("DELETE FROM T_BOX_STATUS;");
    if (ret != zl::CDBConnector::EDBError_Success)
    {
        LOG(ERROR) << "clean all cabinet data failed: " << ret;
        return;
    }

    LOG(INFO) << "clean all cabinet data success";
    return;
}

void zl::CCabinetManager::update_locker_status(ELockerStatus flag, int32_t pcbno, unsigned char * buf, int32_t len)
{
	if (len <= 0)
	{
		LOG(ERROR) << "update locker status failed, invalid buf len: " << flag << " pcbno: " << pcbno;
		return;
	}

    std::unique_lock<std::mutex> locker(mutex_lockers_);
	for (size_t i = 0; i < lockers_.size(); ++i)
	{
		Locker& locker = lockers_[i];
		if (locker.pcbno != pcbno)
		{
			continue;
		}

		int32_t index = locker.index - 1;
		bool data = false;
		if (index < len || index >= 0)
        {
            data = buf[index] == 0x00 ? false : true;
		}
		auto status = calc_locker_status(locker.status, flag, data);
		if (status != locker.status)
		{
			LOG(INFO) << "locker status changed: " << locker.boxno << " " << status;
			emit lockerStatusChanged(locker.boxno);
		}
		locker.status = status;
	}
}

int32_t zl::CCabinetManager::calc_locker_status(int32_t status, ELockerStatus index, bool flag)
{
	if (!flag)
	{
		return (status & (~index));
	}
	else
	{
		return (status | index);
	}
}

Locker zl::CCabinetManager::get_locker(int32_t boxno)
{
	Locker l;
	std::find_if(lockers_.begin(), lockers_.end(), [&](const Locker& locker)->bool {
		if (locker.boxno == boxno)
		{
			l = locker;
			return true;
		}
		return false;
	});

	return l;
}

void zl::CCabinetManager::create_door_control_board_devices(const std::vector<DoorControlBoard> &dcb_list)
{
    std::unique_lock<std::mutex> locker(mutex_device_);
    LOG(INFO) << "=====================================";
    LOG(INFO) << "create door control board devices: " << dcb_list.size();

    // 关闭原有连接
    for (auto& d : device_list_)
    {
        d->Disconnect();
    }
    device_list_.clear();

    // 创建新的设备对象
    zl::zlbase::CDeviceBuilderProducer producer;
    auto builder = producer.GetControlBoardBuilder();
    for (auto& d : dcb_list)
    {
        zl::zlbase::DeviceOption opt;
        opt.protocol_type = d.protocol_type;
        opt.linkoption.type = d.datalinkopt.type;
        opt.linkoption.is_shared = d.datalinkopt.is_shared;
        opt.linkoption.localport = d.datalinkopt.localport;
        opt.linkoption.remoteaddr = d.datalinkopt.remoteaddr;
        opt.linkoption.remoteport = d.datalinkopt.remoteport;
        opt.linkoption.com = d.datalinkopt.com;
        opt.linkoption.baud_rate = d.datalinkopt.baud_rate;
        opt.linkoption.flow_control = d.datalinkopt.flow_control;
        opt.linkoption.parity = d.datalinkopt.parity;
        opt.linkoption.stop_bits = d.datalinkopt.stop_bits;
        opt.linkoption.character_size = d.datalinkopt.character_size;
        opt.linkoption.timeout_conn = d.datalinkopt.timeout_conn;
        opt.linkoption.timeout_read = d.datalinkopt.timeout_read;
        opt.linkoption.timeout_write = d.datalinkopt.timeout_write;

        auto device = builder->Concreate(opt);
        if (device == nullptr)
        {
            LOG(ERROR) << "create device failed: " << (int32_t)d.col;
            continue;
        }

        device->SetCol(d.col);
        device_list_.push_back(device);
        LOG(INFO) << "\r\ndevice col : " << (int32_t)d.col
                  << "\r\nconcreate device: " << device->GetTransport()->GetDatalink()->Origin()
                  << "\r\nconnect device: " << device->Connect();
    }
}

int32_t zl::CCabinetManager::convert_layout_typestr(char typestr)
{
    switch (typestr)
    {
    case 'S':
    case 's':
        return zl::ELockerType_Small;
    case 'M':
    case 'm':
        return zl::ELockerType_Medium;
    case 'L':
    case 'l':
        return zl::ELockerType_Large;
    case 'E':
    case 'e':
        return zl::ELockerType_ExtraLarge;
    default:
        return zl::ELockerType_Default;
    }
}

void zl::CCabinetManager::start_work_thread()
{
    if (work_thread_ != nullptr)
    {
        if (work_thread_->joinable())
        {
            work_thread_->join();
        }
        delete work_thread_;
        work_thread_ = nullptr;
    }

    work_flag_ = true;
    work_thread_ = new std::thread([this]() {
        unsigned char status[128] = { 0x00 };
        size_t len = 127;
        while (work_flag_)
        {
            // 开始巡检
            std::this_thread::sleep_for(std::chrono::seconds(3));
            std::unique_lock<std::mutex> locker(mutex_device_);
            for (auto& d : device_list_)
            {
                auto aaa = d->GetItemStatus(status, len);
                update_locker_status(zl::ELockerStatus_Item, d->GetCol(), status, len);
            }
        }
    });
}

void zl::CCabinetManager::timerEvent(QTimerEvent * event)
{
    // todo
}
